use crossterm::{
  event::{KeyCode, KeyEvent, KeyModifiers},
  terminal, Result,
};
use errno::errno;

use xxx_editor::Position;

use crate::{keyboard::Keyboard, screen::Screen};

#[derive(Debug)]
pub enum EditorKey {
  Left,
  Right,
  Up,
  Down,
}

// impl std::fmt::Display for EditorKey {
//   fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
//     match self {
//       Self::Left => f.write_str("a"),
//       Self::Right => f.write_str("d"),
//       Self::Up => f.write_str("w"),
//       Self::Down => f.write_str("s"),
//     }
//   }
// }

impl From<char> for EditorKey {
  fn from(value: char) -> Self {
    match value {
      'a' => Self::Left,
      'd' => Self::Right,
      'w' => Self::Up,
      's' => Self::Down,
      _ => panic!("Invalid character"),
    }
  }
}

#[derive(Debug)]
pub struct Editor {
  screen: Screen,
  keyboard: Keyboard,
  cursor: Position,
  rows: Vec<String>,
  rowoff: u16,
  coloff: u16,
}

impl Editor {
  // pub fn with_file<P: AsRef<std::path::Path>>(filename: P) -> Result<Self> {
  pub fn with_file<P>(filename: P) -> Result<Self>
  where
    P: AsRef<std::path::Path>,
  {
    let lines = std::fs::read_to_string(filename)
      .expect("Unable to open file")
      .split('\n')
      .map(|v| v.into())
      .collect::<Vec<String>>();

    Self::build(&lines)
  }

  pub fn new() -> Result<Self> {
    Self::build(&[])
  }

  fn build(data: &[String]) -> Result<Self> {
    let rows = if data.is_empty() {
      Vec::new()
    } else {
      Vec::from(data)
    };

    Ok(Self {
      screen: Screen::new()?,
      keyboard: Keyboard {},
      cursor: Position::default(),
      rows,
      rowoff: 0,
      coloff: 0,
    })
  }

  pub fn start(&mut self) -> Result<()> {
    terminal::enable_raw_mode()?;
    loop {
      if self.refresh_screen().is_err() {
        self.die("unable to refresh screen");
      }

      self
        .screen
        .move_to(&self.cursor, self.rowoff, self.coloff)?;
      self.screen.flush()?;

      if self.process_keypress()? {
        break;
      }
    }
    terminal::disable_raw_mode()
  }

  pub fn process_keypress(&mut self) -> Result<bool> {
    if let Ok(c) = self.keyboard.read() {
      match c {
        KeyEvent {
          code: KeyCode::Char('q'),
          modifiers: KeyModifiers::CONTROL,
        } => return Ok(true),
        KeyEvent {
          code: KeyCode::Char(key),
          ..
        } => match key {
          'w' | 'a' | 's' | 'd' => {
            self.move_cursor(key.into());
          }
          _ => {}
        },
        KeyEvent { code, .. } => match code {
          KeyCode::Up => self.move_cursor(EditorKey::Up),
          KeyCode::Down => self.move_cursor(EditorKey::Down),
          KeyCode::Left => self.move_cursor(EditorKey::Left),
          KeyCode::Right => self.move_cursor(EditorKey::Right),
          KeyCode::Home | KeyCode::End => {
            if code == KeyCode::Home {
              self.cursor.x = 0
            } else {
              self.cursor.x = self.screen.bounds().x - 1
            }
          }
          KeyCode::PageUp | KeyCode::PageDown => {
            let bounds = self.screen.bounds();
            for _ in 0..bounds.y {
              self.move_cursor(if code == KeyCode::PageUp {
                EditorKey::Up
              } else {
                EditorKey::Down
              })
            }
          }
          _ => {}
        },
      }
    } else {
      self.die("Unable to read from keyboard");
    }
    Ok(false)
  }

  pub fn refresh_screen(&mut self) -> Result<()> {
    self.scroll();
    self.screen.clear()?;
    self
      .screen
      .draw_rows(&self.rows, self.rowoff, self.coloff)?;

    Ok(())
  }

  pub fn die<S: Into<String>>(&mut self, message: S) {
    let _ = self.screen.clear();
    let _ = terminal::disable_raw_mode();
    eprintln!("{}: {}", message.into(), errno());
    std::process::exit(1);
  }

  fn move_cursor(&mut self, key: EditorKey) {
    let row_idx = if self.cursor.y as usize >= self.rows.len() {
      None
    } else {
      Some(self.cursor.y as usize)
    };

    match key {
      EditorKey::Left => {
        self.cursor.x = self.cursor.x.saturating_sub(1);
      }
      EditorKey::Right => {
        if let Some(idx) = row_idx {
          // 限制行一直往后移动
          if self.rows[idx].len() > self.cursor.x.into() {
            self.cursor.x += 1
          }
        }
      }
      EditorKey::Up => {
        self.cursor.y = self.cursor.y.saturating_sub(1);
      }
      // EditorKey::Down if self.cursor.y <= boudns.y => self.cursor.y += 1,
      EditorKey::Down if self.cursor.y <= self.rows.len() as u16 => {
        self.cursor.y += 1
      }
      // _ => self.die("invalid movement character"),
      _ => {}
    }

    // 光标移动到下一行的位置为当前的位置,下一行长度小于就是最后,每次都会继承上一次的位置到下一行
    let rowlen = if self.cursor.y as usize >= self.rows.len() {
      0
    } else {
      self.rows[self.cursor.y as usize].len() as u16
    };
    self.cursor.x = self.cursor.x.min(rowlen);
  }

  fn scroll(&mut self) {
    let bounds = self.screen.bounds();
    if self.cursor.y < self.rowoff {
      self.rowoff = self.cursor.y;
    }
    if self.cursor.y >= self.rowoff + bounds.y {
      self.rowoff = self.cursor.y - bounds.y + 1;
    }

    if self.cursor.x < self.coloff {
      self.coloff = self.cursor.x;
    }
    if self.cursor.x >= self.coloff + bounds.x {
      self.coloff = self.cursor.x - bounds.x + 1;
    }
  }
}
